package com.NeuglsWorkStudio.Math
{
	import flash.geom.Point;

	/**
	 * Implemented the 2d line class
	 * @author Neugls**/
	public class Line2D
	{
		private var _FXParam:Number; //the parameter of x
		private var _FYParam:Number;//the parameter of y
		private var _FParam:Number; //the parameter of const
		
		public function Line2D(xp:Number,yp:Number,p:Number)
		{
			_FXParam=xp;
			_FYParam=yp;
			_FParam=p;
		}
		
		public function get ConstParam():Number
		{
			return _FParam;
		}

		public function set ConstParam(value:Number):void
		{
			_FParam = value;
		}

		public function get YParam():Number
		{
			return _FYParam;
		}

		public function set YParam(value:Number):void
		{
			_FYParam = value;
		}

		public function get XParam():Number
		{
			return _FXParam;
		}

		public function set XParam(value:Number):void
		{
			_FXParam = value;
		}

		public function get Vector():Vector2D{
			return new Vector2D(-_FYParam,_FXParam);
		}
		
		/**
		 * calculate the distance from the point to the line
		 * @param P the point 
		 * */
		public function DistanceOfPoint(P:Point):Number{
			return Math.abs(P.x*_FXParam+P.y*_FYParam+_FParam)/Math.sqrt(_FXParam*_FXParam+_FYParam*_FYParam); 
		}
		
		/**
		 * calculate the distance from the point to the line, It value can be less than zero
		 * @param P the point 
		 * */
		public function DistanceOfPointNormal(P:Point):Number{
			return (P.x*_FXParam+P.y*_FYParam+_FParam)/Math.sqrt(_FXParam*_FXParam+_FYParam*_FYParam); 
		}
		
		/**
		 * return whether the point is on line
		 * @param P the test point
		 * **/
		public function IsPointOnLine(P:Point):Boolean{
			return GetYFromX(P.x)-P.y==0;
		}
		
		/**
		 * Get the X coordinate from Y
		 * @param Y the Y value
		 * */
		public function GetXFromY(Y:Number):Number{
			return -(_FParam+Y*_FYParam)/_FXParam;
		}
		
		
		/**
		 * Get the Y coordinate from X
		 * @param X the x value
		 * */
		public function GetYFromX(X:Number):Number{
			return -(_FParam+X*_FXParam)/_FYParam;
		}
		
		/**
		 * Get the cross point from the line 
		 * @param line Target line
		 * @return The crossed point
		 * */
		public function GetCrossPointWithLine(line:Line2D):Point{
			var y:Number=(line.XParam*this.ConstParam-line.ConstParam*this.XParam)/(line.YParam*this.XParam-this.YParam*line.XParam);
			var x:Number;
			if(this.XParam==0){
				x=-(line.YParam*y+line.ConstParam)/line.XParam;				
			}else{
				x=-(this.YParam*y+this.ConstParam)/this.XParam;
			}
			return new Point(x,y);
		}
		
		public function Clone():Line2D{
			return Line2D.GetLineFromParameters(_FXParam,_FYParam,_FParam);
		}
		
		/**
		 * return whether the line is parallel with the second line
		 * @param line Target line
		 * @return If parallel then return true else false
		 * */
		public function IsParallelLine(line:Line2D):Boolean{
			return this.Vector.x/this.Vector.y==line.Vector.x/line.Vector.y;
		}
		
		/**
		 * return whether the line is perpendicular to the second line
		 * @param line Target line
		 * @return If Vertical then return true else false
		 * */
		public function IsPerpendicularToLine(line:Line2D):Boolean{
			return this.Vector.dotProd(line.Vector)==0;
		}
		
		/**
		 * return the line's Normals which is perpendicular to the line
		 * @return the Normals Vector
		 * */
		public function GetLineNormals():Vector2D{
			return new Vector2D(1,-this.Vector.x/this.Vector.y);
		}
		
		/**
		 * return the angle bettwen the current and target line. The return value
		 * is rad.
		 * @param line Target line
		 * */
		public function GetLineAngle(line:Line2D):Number{
			var lineVector:Vector2D=line.Vector;
			return Math.acos(this.Vector.dotProd(lineVector)/(this.Vector.length*lineVector.length));
		}
		
		
		public  function toString():String{
			return _FXParam.toString()+'X+'+_FYParam.toString()+'Y+'+_FParam.toString()+'=0';
		}
		
		
		/**Return a new line which symmetric to this line base to the parameter line
		 * <br />获取与本直线关于参数直线对称的直线
		 * @param Axis The axis of symmetric
		 * */
		public function GetSymmetricLine(Axis:Line2D):Line2D{
			var crossPoint:Point=this.GetCrossPointWithLine(Axis);
			var ThisNormalVector:Vector2D=this.Vector.normalize();
			var AxisNormalVector:Vector2D=Axis.Vector.normalize();
			var TargetVector:Vector2D=AxisNormalVector.multiply(ThisNormalVector.dotProd(AxisNormalVector));
			TargetVector.x=2*TargetVector.x-ThisNormalVector.x;
			TargetVector.y=2*TargetVector.y-ThisNormalVector.y;
			return Line2D.GetLineFromVectorAndPoint(TargetVector,crossPoint);
		}
		
		/**
		 * Return a new line which is produced by rotate this line a angle.
		 * @param Pos The rotate point
		 * @param Angle The rotate angle
		 * */
		public function RotateLineAtPoint(Pos:Point,Angle:Number):Line2D{
			var v:Vector2D=this.Vector.clone();
			v.rotate(Angle);
			return Line2D.GetLineFromVectorAndPoint(v,Pos);
		}
					
		/**
		 * Get line form the direction vector and the a point on the line
		 * @param vector The direction vector
		 * @param point The any point on the line
		 * @return the Line2D instance
		 * */
		public static  function GetLineFromVectorAndPoint(vector:Vector2D,point:Point):Line2D{
			return Line2D.GetLineFromParameters(vector.y,-vector.x,-(point.x*vector.y-point.y*vector.x));
		}
		
		/**
		 * Get the line from any diffirent point on the line
		 * @parma point1 The first point
		 * @param point2 The second point
		 * @return The Line2D instance
		 * */
		public static function GetLineFromTwoPoint(point1:Point,point2:Point):Line2D{
			var v:Vector2D=new Vector2D(point2.x-point1.x,point2.y-point1.y);
			return Line2D.GetLineFromVectorAndPoint(v,point1);
		}
		
		
		/**
		 * Get line from the x,y parameters and consts
		 * @param xp The parameter of the x
		 * @param yp The parameter of the y
		 * @param p The parameter of const
		 * @return The Line2D instance
		 * */
		public static function GetLineFromParameters(xp:Number,yp:Number,p:Number):Line2D{
			return new Line2D(xp,yp,p);
		}
		
		
		
		/**
		 * Return the X Axis
		 * */
		public static function XAxis():Line2D{
			return new Line2D(0,1,0);
		}
		
		/**
		 * Return the Y Axis
		 * */
		public static function YAxis():Line2D{
			return new Line2D(1,0,0);
		}
	}
}